#include "StdAfx.h"
#include "command.h"
#include "RegionScene.h"
#include "regionitem.h"

	class CommandPrivate
	{
	public:
		CommandPrivate(Region* item)
		{
			regionItems << item;
		}
		CommandPrivate(const QList<Region*>& items)
			: regionItems(items)
		{
		}
		QList<Region*> regionItems;
	};

	Command::Command(Region* item, QUndoCommand *parent)
		: QUndoCommand(parent)
		, d_ptr(new CommandPrivate(item))
	{
		
	}

	Command::Command(CommandPrivate &dd, QUndoCommand *parent)
		: QUndoCommand(parent)
		, d_ptr(&dd)
	{
	}

	Command::~Command()
	{
	}



	Region* Command::regionItem() const
	{
		return d_func()->regionItems.value(0);
	}

	QList<Region*> Command::regionItems() const
	{
		return d_func()->regionItems;
	}

	class AddCommandPrivate : public CommandPrivate
	{
	public:
		AddCommandPrivate(RegionScene *scene, Region* item)
			: CommandPrivate(item)
			, scene(scene)
		{
		}
		RegionScene *scene;
	};

	AddCommand::AddCommand(RegionScene *scene, Region* item, QUndoCommand *parent)
		: Command(*new AddCommandPrivate(scene, item), parent)
	{

	}

	AddCommand::~AddCommand()
	{

	}


	RegionScene* 
		AddCommand::regionScene() const
	{
		return d_func()->scene;
	}

	void 
		AddCommand::undo()
	{
		setText(QObject::tr("Remove Region"));
		regionScene()->removeRegion(regionItem());
	}

	void 
		AddCommand::redo()
	{
		setText(QObject::tr("Add Region"));	
		regionScene()->addRegion(regionItem());
	}


	DelCommand::DelCommand(RegionScene *scene, Region* item, QUndoCommand *parent)
		: AddCommand(scene,item,parent)
	{

	}
	DelCommand::~DelCommand()
	{

	}

	void 
		DelCommand::undo()
	{
		AddCommand::redo();
	}

	void 
		DelCommand::redo()
	{	
		AddCommand::undo();
	}

	struct MoveCommandPrivate : public CommandPrivate
	{
	public:
		MoveCommandPrivate(Region* item, const QPointF& distance)
			: CommandPrivate(item)
			, distance(distance)
		{

		}

		MoveCommandPrivate(const QList<Region*>& items, const QPointF& distance)
			: CommandPrivate(items)
			, distance(distance)
		{

		}
		QPointF distance;
	};

	MoveCommand::MoveCommand(Region* item, const QPointF& distance, QUndoCommand *parent)
		: Command(*new MoveCommandPrivate(item, distance),parent)
	{

	}

	MoveCommand::MoveCommand(const QList<Region*>& items, const QPointF& distance, QUndoCommand *parent)
		: Command(*new MoveCommandPrivate(items, distance), parent)
	{

	}

	MoveCommand::~MoveCommand()
	{

	}

	QPointF 
		MoveCommand::distance() const
	{
		return d_func()->distance;
	}

	void 
		MoveCommand::setDistance(const QPointF& distance)
	{
		d_func()->distance = distance;
	}

	void 
		MoveCommand::undo()
	{
		setText(QObject::tr("Move Position"));
		foreach(Region* item,regionItems())
		{
			item->moveBy(-distance());
		}

	}

	void 
		MoveCommand::redo()
	{
		setText(QObject::tr("Restore Position"));
		foreach(Region* item,regionItems())
		{
			item->moveBy(distance());
		}
	}

	bool 
		MoveCommand::mergeWith(const QUndoCommand *command)
	{
		Q_D(MoveCommand);
		if (command->id() != CommandId)
			return false;

		const MoveCommand *other = static_cast<const MoveCommand*>(command);
		if (regionItems() != other->regionItems())
			return false;

		d->distance += other->distance();
		return true;
	}


	class RotateItemCommandPrivate : public CommandPrivate
	{
	public:
		RotateItemCommandPrivate(Region* item, const qreal rotateAngle)
			: CommandPrivate(item)
			, rotateAngle(rotateAngle)
		{

		}
		RotateItemCommandPrivate(const QList<Region*>& items, const qreal rotateAngle)
			: CommandPrivate(items)
			, rotateAngle(rotateAngle)
		{

		}
		qreal rotateAngle;
	};

	RotateItemCommand::RotateItemCommand(Region* item, const qreal rotateAngle, QUndoCommand *parent)
		: Command(*new RotateItemCommandPrivate(item, rotateAngle), parent)
	{
	}

	RotateItemCommand::RotateItemCommand(const QList<Region*>& items, const qreal rotateAngle, QUndoCommand *parent)
		: Command(*new RotateItemCommandPrivate(items, rotateAngle), parent)
	{

	}

	RotateItemCommand::~RotateItemCommand()
	{

	}

	qreal 
		RotateItemCommand::rotateAngle() const
	{
		return d_func()->rotateAngle;
	}

	void 
		RotateItemCommand::setRotateAngle(const qreal& rotateAngle)
	{
		Q_D(RotateItemCommand);
		d->rotateAngle = rotateAngle;
	}

	void 
		RotateItemCommand::undo()
	{
		setText(QObject::tr("Rotate Degree"));
		foreach(Region* item,regionItems())
		{
			qreal rotateAngle = regionItem()->rotation() - this->rotateAngle();
			while(qAbs(rotateAngle) > qreal(360.))
			{
				rotateAngle -= 360.;
			}

			item->setRotation(rotateAngle);
		}

	}

	void 
		RotateItemCommand::redo()
	{
		setText(QObject::tr("Rotate Degree"));
		foreach(Region* item,regionItems())
		{
			qreal rotateAngle = regionItem()->rotation() + this->rotateAngle();
			if(qAbs(rotateAngle) > qreal(360.))
			{
				rotateAngle -= 360.;
			}

			item->setRotation(rotateAngle);
		}
	}

	bool 
		RotateItemCommand::mergeWith(const QUndoCommand *command)
	{
		Q_D(RotateItemCommand);
		if (command->id() != CommandId)
			return false;

		const RotateItemCommand *other = static_cast<const RotateItemCommand*>(command);
		if (regionItem() != other->regionItem())
			return false;

		d->rotateAngle += other->rotateAngle();
		return true;
	}

	class RectangleAdjustCommandPrivate : public CommandPrivate
	{
	public:
		RectangleAdjustCommandPrivate(Rectangle* item, 
									  const QPointF& newPos, 
									  const qreal newRotation, 
									  const QSizeF& newSize)
			: CommandPrivate(item)
			, rectangleItem(item)
			, newPos(newPos)
			, newRotation(newRotation)
			, newSize(newSize)
		{
			oldPos = item->pos();
			oldRotation = item->rotation();
			oldSize = item->rectangleSize();
		}
		Rectangle* rectangleItem;
		QPointF oldPos;
		QPointF newPos;
		qreal oldRotation;
		qreal newRotation;
		QSizeF newSize;
		QSizeF oldSize;
	};

	RectangleAdjustCommand::RectangleAdjustCommand(Rectangle* rectangleItem, 
												   const QPointF& newPos, 
												   const qreal newRotation, 
												   const QSizeF& newSize, QUndoCommand *parent)
		: Command(*new RectangleAdjustCommandPrivate(rectangleItem, 
														 newPos, 
														 newRotation, 
														 newSize), 
					  parent)
	{
	}

	RectangleAdjustCommand::~RectangleAdjustCommand()
	{

	}

	QPointF 
		RectangleAdjustCommand::oldPos() const
	{
		return d_func()->oldPos;
	}

	void 
		RectangleAdjustCommand::setOldPos(const QPointF& pos)
	{
		Q_D(RectangleAdjustCommand);
		d->oldPos = pos;
	}

	QPointF 
		RectangleAdjustCommand::newPos() const
	{
		return d_func()->newPos;
	}

	void 
		RectangleAdjustCommand::setNewPos(const QPointF& pos)
	{
		Q_D(RectangleAdjustCommand);
		d->newPos = pos;
	}

	qreal 
		RectangleAdjustCommand::oldRotation() const
	{
		return d_func()->oldRotation;
	}

	void 
		RectangleAdjustCommand::setOldRotation(const qreal oldRotation)
	{
		Q_D(RectangleAdjustCommand);
		d->oldRotation = oldRotation;
	}

	qreal 
		RectangleAdjustCommand::newRotation() const
	{	
		return d_func()->newRotation;
	}

	void 
		RectangleAdjustCommand::setNewRotation(const qreal newRotation)
	{
		Q_D(RectangleAdjustCommand);
		d->newRotation = newRotation;
	}

	QSizeF 
		RectangleAdjustCommand::newSize() const
	{
		return d_func()->newSize;
	}

	void 
		RectangleAdjustCommand::setNewSize(const QSizeF& newSize)
	{
		Q_D(RectangleAdjustCommand);
		d->newSize = newSize;
	}

	QSizeF 
		RectangleAdjustCommand::oldSize() const
	{
		return d_func()->oldSize;
	}

	void 
		RectangleAdjustCommand::setOldSize(const QSizeF& oldSize)
	{
		Q_D(RectangleAdjustCommand);
		d->oldSize = oldSize;
	}

	Rectangle* 
	RectangleAdjustCommand::rectangleItem() const
	{
		return d_func()->rectangleItem;
	}

	void 
		RectangleAdjustCommand::undo()
	{
		QString text = QObject::tr("RectangleAdjust,Pos:(%1,%2),Size:(%3,%4),Rotation:%5")
						.arg(newPos().x())
						.arg(newPos().y())
						.arg(newSize().width())
						.arg(newSize().height())
						.arg(newRotation());
		setText(text);
		rectangleItem()->setRotation(oldRotation());
		rectangleItem()->setRectangleSize(oldSize());
		rectangleItem()->setPos(oldPos());
		

	}

	void 
		RectangleAdjustCommand::redo()
	{
		QString text = QObject::tr("RectangleAdjust,Pos:(%1,%2),Size:(%3,%4),Rotation:%5")
						.arg(newPos().x())
						.arg(newPos().y())
						.arg(newSize().width())
						.arg(newSize().height())
						.arg(newRotation());
		setText(text);
		rectangleItem()->setRotation(newRotation());
		rectangleItem()->setRectangleSize(newSize());
		rectangleItem()->setPos(newPos());
		
	}

	bool 
	RectangleAdjustCommand::mergeWith(const QUndoCommand *command)
	{
		Q_D(RectangleAdjustCommand);
		if (command->id() != CommandId)
			return false;

		const RectangleAdjustCommand *other = static_cast<const RectangleAdjustCommand*>(command);
		if (rectangleItem() != other->rectangleItem())
			return false;
		d->newSize = other->newSize();
		d->newRotation = other->newRotation();
		d->newPos = other->newPos();
		return true;
	}

	class AdjustRingCommandPrivate : public CommandPrivate
	{
	public:
		AdjustRingCommandPrivate(Ring* item, const QSizeF& newRingSize)
			: CommandPrivate(item)
			, ringItem(item)
			, newRingSize(newRingSize)
		{

		}
		Ring* ringItem;
		QSizeF newRingSize;
		QSizeF oldRingSize;
	};

	AdjustRingCommand::AdjustRingCommand(Ring* item, const QSizeF& newRingSize, QUndoCommand *parent)
		: Command(*new AdjustRingCommandPrivate(item, newRingSize), parent)
	{
		setOldRingSize(item->ringSize());
	}

	AdjustRingCommand::~AdjustRingCommand()
	{

	}

	Ring*
		AdjustRingCommand::ringItem() const
	{
		return d_func()->ringItem;
	}

	QSizeF 
		AdjustRingCommand::newRingSize() const
	{
		return d_func()->newRingSize;
	}

	void 
		AdjustRingCommand::setNewRingSize(const QSizeF& newRingSize)
	{
		d_func()->newRingSize = newRingSize;
	}

	QSizeF 
		AdjustRingCommand::oldRingSize() const
	{
		return d_func()->oldRingSize;
	}

	void 
		AdjustRingCommand::setOldRingSize(const QSizeF& oldRingSize)
	{
		d_func()->oldRingSize = oldRingSize;
	}

	void 
		AdjustRingCommand::undo()
	{
		setText(QObject::tr("Adjust Ring Size"));
		ringItem()->setRingSize(oldRingSize());
	}

	void 
		AdjustRingCommand::redo()
	{
		setText(QObject::tr("Restore Ring Size"));
		ringItem()->setRingSize(newRingSize());
	}

	bool 
		AdjustRingCommand::mergeWith(const QUndoCommand *command)
	{
		if (command->id() != CommandId)
			return false;

		const AdjustRingCommand *other = static_cast<const AdjustRingCommand*>(command);
		if (ringItem() != other->ringItem())
			return false;
		d_func()->newRingSize = other->newRingSize();
		return true;
	}

	struct AdjustPolygonCommandPrivate : public CommandPrivate
	{
		AdjustPolygonCommandPrivate(PolygonItem* item, 
									const int index, 
									const QPointF& distance)
			: CommandPrivate(item)
			, item(item)
			, index(index)
			, distance(distance)
		{
		}
		PolygonItem* item;
		const int index;
		QPointF distance;
	};

	AdjustPolygonCommand::AdjustPolygonCommand(PolygonItem* item, 
											   const int index, 
											   const QPointF& distance, 
											   QUndoCommand *parent)
		: Command(*new AdjustPolygonCommandPrivate(item, index, distance), parent)
	{
	}

	AdjustPolygonCommand::~AdjustPolygonCommand()
	{

	}

	PolygonItem*
		AdjustPolygonCommand::polygonItem() const
	{
		return d_func()->item;
	}

	int 
		AdjustPolygonCommand::index() const
	{
		return d_func()->index;
	}

	QPointF 
		AdjustPolygonCommand::distance() const
	{
		return d_func()->distance;
	}

	void 
		AdjustPolygonCommand::setDistance(const QPointF& distance)
	{
		d_func()->distance = distance;
	}

	void 
		AdjustPolygonCommand::undo()
	{
		setText(QObject::tr("Relocate Point"));
		polygonItem()->refPolygon()[index()] -= distance();
	}

	void 
		AdjustPolygonCommand::redo()
	{
		setText(QObject::tr("Restore Point"));
		polygonItem()->refPolygon()[index()] += distance();
	}

	bool 
		AdjustPolygonCommand::mergeWith(const QUndoCommand *command)
	{
		Q_D(AdjustPolygonCommand);
		if (command->id() != CommandId)
			return false;

		const AdjustPolygonCommand *other = static_cast<const AdjustPolygonCommand*>(command);
		if (polygonItem() != other->polygonItem())
			return false;
		d->distance += other->distance();
		return true;
	}


	struct RegionGroupCommandPrivate : public CommandPrivate
	{
		RegionGroupCommandPrivate(RegionScene* scene, const QList<Region*>& items)
			: CommandPrivate(items)
			, scene(scene)
			, group(0)
		{
		}

		RegionGroupCommandPrivate(RegionScene* scene, RegionGroup* regionGroup)
			: CommandPrivate(regionGroup->childregionItems())
			, scene(scene)
			, group(regionGroup)
		{

		}
		RegionScene* scene;
		RegionGroup* group;
	};

	RegionGroupCommand::RegionGroupCommand(RegionScene* scene, const QList<Region*>& items, QUndoCommand *parent)
		: Command(*new RegionGroupCommandPrivate(scene, items), parent)
	{
	}

	RegionGroupCommand::RegionGroupCommand(RegionScene* scene, RegionGroup* group, QUndoCommand *parent)
		: Command(*new RegionGroupCommandPrivate(scene, group), parent)
	{
	}

	RegionGroupCommand::~RegionGroupCommand()
	{

	}

	RegionScene* RegionGroupCommand::regionScene() const
	{
		return d_func()->scene;
	}

	RegionGroup* RegionGroupCommand::regionGroup() const
	{
		return qgraphicsitem_cast<RegionGroup*>(regionItem());
	}

	void RegionGroupCommand::undo()
	{
		Q_D(RegionGroupCommand);
		setText(QObject::tr("Ungroup regions"));
		regionScene()->destroyRegionGroup(d->group);
	}

	void RegionGroupCommand::redo()
	{		
		Q_D(RegionGroupCommand);
		setText(QObject::tr("Group regions"));
		RegionGroup* group = regionScene()->createRegionGroup(regionItems());

		d->group = group;
	}


	RegionUngroupCommand::RegionUngroupCommand(RegionScene* scene, RegionGroup* regionGroup, QUndoCommand *parent)
		: RegionGroupCommand(scene, regionGroup, parent)
	{
	}

	RegionUngroupCommand::~RegionUngroupCommand()
	{

	}

	void RegionUngroupCommand::undo()
	{
		RegionGroupCommand::redo();
	}

	void RegionUngroupCommand::redo()
	{	
		RegionGroupCommand::undo();
		
	}
